//
// Created by yunnan xu on 2020/12/14.
//

#include "ShadowMapPass.h"
#include "Pass.h"
#include "Texture.h"
#include "EngineCore.h"
#include "Scene.h"
#include "RenderItem.h"
#include "RenderItemStaticOpaque.h"
#include "Model.h"
#include <EGL/egl.h>
#include <GLES3/gl32.h>
#include <memory>
#include <unordered_map>

ShadowMapPass<GraphicsAPI::GLESv3>::ShadowMapPass()
{

    width = 1024;
    height = 1024;

    std::unordered_map<int, int> tex2d_params = {
            {GL_TEXTURE_MIN_FILTER, GL_NEAREST},
            {GL_TEXTURE_MAG_FILTER, GL_NEAREST},
            {GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE},
            {GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE},
            {GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL},
            {GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE}
    };

    depthTexture = std::make_shared<Texture2D<GraphicsAPI::GLESv3>> (GL_DEPTH_COMPONENT32F, 1, width, height, &tex2d_params, 1);

    GL_CHECK(glGenFramebuffers(1, &framebuffer));
    GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
    GL_CHECK(glFramebufferTexture2D(GL_FRAMEBUFFER,
                           GL_DEPTH_ATTACHMENT,
                           GL_TEXTURE_2D,
                           depthTexture->GetTexture(),
                           0));
    GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}

void ShadowMapPass<GraphicsAPI::GLESv3>::InitShaderUniformLocation()
{
    lightMVP_location = glGetUniformLocation(program, "lightMVP");
}

void ShadowMapPass<GraphicsAPI::GLESv3>::Draw()
{
    GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
    GL_CHECK(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT));
    GL_CHECK(glEnable(GL_CULL_FACE));
    GL_CHECK(glEnable(GL_DEPTH_TEST));
    GL_CHECK(glDepthMask(GL_TRUE));
    GL_CHECK(glViewport(0, 0, width, height));
    // GL_CHECK(glCullFace(GL_FRONT));
    GL_CHECK(glUseProgram(program));
    GL_CHECK(glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE));

    auto curr_scene = SceneMgr::GetInstance()->GetCurrentScene();
    glm::mat4 vp_matrix = curr_scene->GetLightMatrix(0);

    for (auto iter = curr_scene->GetOpaqueItemBegin(); iter != curr_scene->GetOpaqueItemEnd(); ++ iter)
    {
        auto gles_item = std::dynamic_pointer_cast<RenderItemStaticOpaque<GraphicsAPI::GLESv3>>(*iter);
        auto mvp_matrix = vp_matrix * gles_item->GetModel()->GetModelMatrix();
        GLfloat * pSource = glm::value_ptr(mvp_matrix);
        GL_CHECK(glUniformMatrix4fv(lightMVP_location, 1, false, pSource));
        GL_CHECK(glBindVertexArray(gles_item->GetVAO()));
        GL_CHECK(glDrawElements(GL_TRIANGLES, gles_item->GetModel()->GetFaceNumber() * 3, gles_item->GetModel()->GetIndexType(), 0));
        GL_CHECK(glBindVertexArray(0));
    }
    GL_CHECK(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
    // GL_CHECK(glCullFace(GL_BACK));
    GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
    GL_CHECK(glViewport(0, 0, g_engine->width, g_engine->height));
}
